﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using ControleRepresentante.Common;
using ControleRepresentante.Logic;
using ControleRepresentante.Logic.Models;
using ControleRepresentante.Logic.Repositories;
using ControleRepresentante.Logic.Statistics;
using Telerik.Web.Mvc;
using WebSite.Controllers.ControllerExtensions;
using WebSite.ViewModels;
using WebSite.ViewModels.DetailsViewModels;
using WebSite.ViewModels.IndexViewModels;

namespace WebSite.Controllers
{
    public abstract class DetailsHolderControllerBase<TRepository, TModel, TStatistics> : AttributeHolderControllerBase<TRepository, TModel, TStatistics>
        where TRepository : class, IDetailsHolderRepository<TModel, TStatistics>
        where TModel : DetailsHolder 
        where TStatistics : IStatistics
    {
        protected DetailsHolderControllerBase(IRepositoryProvider repositoryProvider, ISessionConfiguration sessionConfiguration, IControllerExtensionProvider controllerExtensionProvider)
            : base(repositoryProvider, sessionConfiguration, controllerExtensionProvider)
        {
        }

        protected sealed override AttributeHolderDetailsViewModel GetAttributeHolderDetailsViewModel(TModel model)
        {
            return DoGetDetailsViewModel(model);
        }

        protected abstract DetailsHolderDetailsViewModel DoGetDetailsViewModel(TModel model);

        protected void SetDetailsSearchValues(IDetailsHolderIndexViewModel detailsHolderIndexViewModel, string cidade, string bairro)
        {
            detailsHolderIndexViewModel.CidadeBuscaValue = cidade;
            detailsHolderIndexViewModel.BairroBuscaValue = bairro;
        }

        protected override void RegisterIndexColumns(IIndexColumnsProvider indexColumnsProvider)
        {
            base.RegisterIndexColumns(indexColumnsProvider);

            indexColumnsProvider.RegisterColumn("Rua", DetailsHolderIndexColumns.Rua);
            indexColumnsProvider.RegisterColumn("Número (Endereço)", DetailsHolderIndexColumns.Numero);
            indexColumnsProvider.RegisterColumn("Complemento (Endereço)", DetailsHolderIndexColumns.Complemento);
            indexColumnsProvider.RegisterColumn("Bairro", DetailsHolderIndexColumns.Bairro);
            indexColumnsProvider.RegisterColumn("CEP", DetailsHolderIndexColumns.CEP);
            indexColumnsProvider.RegisterColumn("Cidade", DetailsHolderIndexColumns.Cidade);
            indexColumnsProvider.RegisterColumn("Estado", DetailsHolderIndexColumns.Estado);

            indexColumnsProvider.RegisterColumn("Telefone", DetailsHolderIndexColumns.Telefone);
            indexColumnsProvider.RegisterColumn("Fax", DetailsHolderIndexColumns.Fax);
            indexColumnsProvider.RegisterColumn("E-mail", DetailsHolderIndexColumns.Email);
            indexColumnsProvider.RegisterColumn("Celular", DetailsHolderIndexColumns.Celular);
        }

        [HttpPost]
        public ActionResult SearchCidade(string text)
        {
            return new JsonResult { Data = Repository.SearchCidades(text) };
        }

        [HttpPost]
        public ActionResult SearchBairro(string text)
        {
            return new JsonResult { Data = Repository.SearchBairros(text) };
        }

        #region Contato

        [GridAction]
        public ActionResult ContatoSelect()
        {
            return GetView(Model.Contatos);
        }

        [GridAction]
        [HttpPost]
        public ActionResult ContatoInsert(string tipo, string valor, string observacao, bool principal)
        {
            Contato contato = new Contato();
            if (TryUpdateContato(contato, tipo, valor, observacao, principal))
            {
                Model.AddContato(contato);
            }
            return GetView(Model.Contatos);
        }

        [GridAction]
        [HttpPost]
        public ActionResult ContatoDelete(Guid contatoId)
        {
            Contato contato = Model.Contatos.Single(x => x.Id == contatoId);
            Model.RemoveContato(contato);
            if (contato.Principal)
            {
                Contato principalForTipo = Model.Contatos.FirstOrDefault(x => x.Tipo == contato.Tipo);
                if (principalForTipo != null)
                {
                    principalForTipo.Principal = true;
                }
            }
            return GetView(Model.Contatos);
        }

        [GridAction]
        [HttpPost]
        public ActionResult ContatoUpdate(Guid contatoId, string tipo, string valor, string observacao, bool principal)
        {
            Contato contato = Model.Contatos.Single(x => x.Id == contatoId);
            TryUpdateContato(contato, tipo, valor, observacao, principal);
            return GetView(Model.Contatos);
        }

        private ActionResult GetView(IEnumerable<Contato> contatos)
        {
            return View(new GridModel(contatos.Select(x => new ContatoViewModel(x))));
        }

        private bool TryUpdateContato(Contato contato, string tipo, string valor, string observacao, bool principal)
        {
            contato.Tipo = tipo.ParseToEnum<ContatoType>();
            contato.Valor = valor;
            contato.Observacao = observacao;
            if (contato.Valor != null && !String.IsNullOrEmpty(contato.Valor))
            {
                Contato principalContatoForTipo = Model.Contatos.FirstOrDefault(x => x.Tipo == contato.Tipo && x.Principal && x.Id != contato.Id);

                if (principalContatoForTipo == null)
                {
                    contato.Principal = true;
                }
                else if (principal)
                {
                    contato.Principal = true;
                    principalContatoForTipo.Principal = false;
                }
                else
                {
                    contato.Principal = false;
                }
                return true;
            }
            return false;
        }

        #endregion

        #region Conta

        [GridAction]
        public ActionResult ContaSelect()
        {
            return GetView(Model.Contas);
        }

        [GridAction]
        [HttpPost]
        public ActionResult ContaInsert(string banco, string agencia, string numero, string observacao, bool principal)
        {
            Conta conta = new Conta();
            if (TryUpdateConta(conta, banco, agencia, numero, observacao, principal))
            {
                Model.AddConta(conta);
            }
            return GetView(Model.Contas);
        }

        [GridAction]
        [HttpPost]
        public ActionResult ContaDelete(Guid contaId)
        {
            Conta conta = Model.Contas.Single(x => x.Id == contaId);
            Model.RemoveConta(conta);
            if (conta.Principal)
            {
                Conta principal = Model.Contas.FirstOrDefault();
                if (principal != null)
                {
                    principal.Principal = true;
                }
            }
            return GetView(Model.Contas);
        }

        [GridAction]
        [HttpPost]
        public ActionResult ContaUpdate(Guid contaId, string banco, string agencia, string numero, string observacao, bool principal)
        {
            Conta conta = Model.Contas.Single(x => x.Id == contaId);
            TryUpdateConta(conta, banco, agencia, numero, observacao, principal);
            return GetView(Model.Contas);
        }

        private ActionResult GetView(IEnumerable<Conta> contas)
        {
            return View(new GridModel(contas.Select(x => new ContaViewModel(x))));
        }

        private bool TryUpdateConta(Conta conta, string banco, string agencia, string numero, string observacao, bool principal)
        {
            conta.Banco = banco;
            conta.Agencia = agencia;
            conta.Numero = numero;
            conta.Observacao = observacao;
            if (conta.Banco != null && !String.IsNullOrEmpty(conta.Banco) &&
                conta.Agencia != null && !String.IsNullOrEmpty(conta.Agencia) &&
                conta.Numero != null && !String.IsNullOrEmpty(conta.Numero))
            {
                Conta principalConta = Model.Contas.FirstOrDefault(x => x.Principal && x.Id != conta.Id);

                if (principalConta == null)
                {
                    conta.Principal = true;
                }
                else if (principal)
                {
                    conta.Principal = true;
                    principalConta.Principal = false;
                }
                else
                {
                    conta.Principal = false;
                }
                return true;
            }
            return false;
        }

        #endregion

        #region Endereco

        [GridAction]
        public ActionResult EnderecoSelect()
        {
            return GetView(Model.Enderecos);
        }

        [GridAction]
        [HttpPost]
        public ActionResult EnderecoInsert(string tipo, string rua, string numero, string complemento, string bairro, string cep, string cidade, string estado, string observacao, bool principal)
        {
            Endereco endereco = new Endereco();
            if (TryUpdateEndereco(endereco, tipo, rua, numero, complemento, bairro, cep, cidade, estado, observacao, principal))
            {
                Model.AddEndereco(endereco);
            }
            return GetView(Model.Enderecos);
        }

        [GridAction]
        [HttpPost]
        public ActionResult EnderecoDelete(Guid enderecoId)
        {
            Endereco endereco = Model.Enderecos.Single(x => x.Id == enderecoId);
            Model.RemoveEndereco(endereco);
            if (endereco.Principal)
            {
                Endereco principalForTipo = Model.Enderecos.FirstOrDefault(x => x.Tipo == endereco.Tipo);
                if (principalForTipo != null)
                {
                    principalForTipo.Principal = true;
                }
            }
            return GetView(Model.Enderecos);
        }

        [GridAction]
        [HttpPost]
        public ActionResult EnderecoUpdate(Guid enderecoId, string tipo, string rua, string numero, string complemento, string bairro, string cep, string cidade, string estado, string observacao, bool principal)
        {
            Endereco endereco = Model.Enderecos.Single(x => x.Id == enderecoId);
            TryUpdateEndereco(endereco, tipo, rua, numero, complemento, bairro, cep, cidade, estado, observacao, principal);
            return GetView(Model.Enderecos);
        }

        private ActionResult GetView(IEnumerable<Endereco> enderecos)
        {
            return View(new GridModel(enderecos.Select(x => new EnderecoViewModel(x))));
        }

        private bool TryUpdateEndereco(Endereco endereco, string tipo, string rua, string numero, string complemento, string bairro, string cep, string cidade, string estado, string observacao, bool principal)
        {
            endereco.Tipo = tipo.ParseToEnum<EnderecoType>();
            endereco.Rua = rua;
            endereco.Numero = numero;
            endereco.Complemento = complemento;
            endereco.Bairro = bairro;
            endereco.CEP = cep;
            endereco.Cidade = cidade;
            endereco.Estado = estado;
            endereco.Observacao = observacao;
            if (endereco.Rua != null && !String.IsNullOrEmpty(endereco.Rua) &&
                endereco.Numero != null && !String.IsNullOrEmpty(endereco.Numero) &&
                endereco.Cidade != null && !String.IsNullOrEmpty(endereco.Cidade) &&
                endereco.Estado != null && !String.IsNullOrEmpty(endereco.Estado))
            {
                DetailsHolder detailsHolder = Model;
                Endereco principalEnderecoForTipo = detailsHolder.Enderecos.FirstOrDefault(x => x.Tipo == endereco.Tipo && x.Principal && x.Id != endereco.Id);

                if (principalEnderecoForTipo == null)
                {
                    endereco.Principal = true;
                }
                else if (principal)
                {
                    endereco.Principal = true;
                    principalEnderecoForTipo.Principal = false;
                }
                else
                {
                    endereco.Principal = false;
                }
                return true;
            }
            return false;
        }

        #endregion
    }
}